package simple

import (
	"bytes"
	"context"
	"encoding/json"
	"gluenet/internal/agent/driver/k8sdriver/data"
	"gluenet/internal/agent/driver/k8sdriver/option"
	"io"
	"os"
	"strings"

	gluemetav1 "gluenet/internal/agent/api/meta/v1"

	"github.com/spf13/viper"
	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/serializer/yaml"
	"k8s.io/apimachinery/pkg/types"
	yamlUtil "k8s.io/apimachinery/pkg/util/yaml"
	v1 "k8s.io/client-go/applyconfigurations/core/v1"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/restmapper"
	"k8s.io/client-go/tools/clientcmd"
)

type SimpleK8sDriver struct {
	ctx context.Context
	kcs *kubernetes.Clientset
	dc  dynamic.Interface
}

func NewSimpleK8sDriver(kubeConfigPath string) (*SimpleK8sDriver, error) {

	var (
		kcfg *rest.Config
		err  error
	)

	if kubeConfigPath != "" {
		kcfg, err = clientcmd.BuildConfigFromFlags("", kubeConfigPath)
	} else {
		kcfg, err = rest.InClusterConfig()
	}

	if err != nil {
		return nil, err
	}

	kcs, err := kubernetes.NewForConfig(kcfg)
	if err != nil {
		return nil, err
	}

	dc, err := dynamic.NewForConfig(kcfg)
	if err != nil {
		return nil, err
	}

	return &SimpleK8sDriver{
		ctx: context.Background(),
		kcs: kcs,
		dc:  dc,
	}, nil
}

func (r *SimpleK8sDriver) IsNameSpaceExist(nameSpace string) bool {
	if _, err := r.kcs.CoreV1().Namespaces().Get(r.ctx, nameSpace, metav1.GetOptions{}); err != nil {
		return false
	}
	return true
}

func (r *SimpleK8sDriver) CreateNameSpace(nameSpace string) error {
	_, err := r.kcs.CoreV1().Namespaces().Create(r.ctx, &corev1.Namespace{
		ObjectMeta: metav1.ObjectMeta{
			Name: nameSpace,
		},
	}, metav1.CreateOptions{})

	return err
}

func (r *SimpleK8sDriver) DeleteNameSpace(namespace string) error {
	return r.kcs.CoreV1().Namespaces().Delete(r.ctx, namespace, metav1.DeleteOptions{})
}

func (r *SimpleK8sDriver) ApplyService(nameSpace string, service *v1.ServiceApplyConfiguration) (*corev1.Service, error) {
	return r.kcs.CoreV1().Services(nameSpace).Apply(r.ctx, service, metav1.ApplyOptions{})
}

func (r *SimpleK8sDriver) DeleteService(nameSpace, serviceName string) error {
	return r.kcs.CoreV1().Services(nameSpace).Delete(r.ctx, serviceName, metav1.DeleteOptions{})
}

func (r *SimpleK8sDriver) ListNodes(opts option.ListOptions) (*data.SimpleNodesList, error) {
	rlt, err := r.kcs.CoreV1().Nodes().List(r.ctx, metav1.ListOptions{})
	return &data.SimpleNodesList{NodeList: rlt}, err
}

func (r *SimpleK8sDriver) ListPods(namespace string, opts option.ListOptions) *data.SimplePodListResult {
	rlt, err := r.kcs.CoreV1().Pods(namespace).List(r.ctx, metav1.ListOptions{})
	return &data.SimplePodListResult{PodList: rlt, Err: err}
}

func (r *SimpleK8sDriver) GetPod(namespace, name string) (*data.SimplePod, error) {
	rlt, err := r.kcs.CoreV1().Pods(namespace).Get(r.ctx, name, metav1.GetOptions{})
	return &data.SimplePod{Pod: rlt}, err
}

// 遍历filePath的文件夹，apply里面所有的yaml
func (r *SimpleK8sDriver) ApplyFromDir(filePath string, opts *gluemetav1.ApplyOptions) error {
	var err error
	//读取路径下所有文件
	dir, err := os.ReadDir(filePath)
	if err != nil {
		return err
	}
	//只读取根目录下的yaml文件
	for _, fi := range dir {
		if fi.IsDir() {
			continue
		}
		fBytes, err := os.ReadFile(filePath + string(os.PathSeparator) + fi.Name())
		if err != nil {
			return err
		} else {
			return r.Apply(bytes.NewReader(fBytes), opts)
		}
	}
	return err
}

// 遍历filePath的文件夹，删除其中yaml文件安装的service
func (r *SimpleK8sDriver) DeleteFromDir(filePath string, opts *gluemetav1.DeleteOptions) error {
	var err error
	//读取路径下的所有文件
	dir, err := os.ReadDir(filePath)
	if err != nil {
		return err
	}
	for _, fi := range dir {
		if fi.IsDir() {
			continue
		}
		fBytes, err := os.ReadFile(filePath + string(os.PathSeparator) + fi.Name())
		if err != nil {
			return err
		} else {
			return r.Delete(bytes.NewReader(fBytes), opts)
		}
	}
	return err
}

func (r *SimpleK8sDriver) Apply(br io.Reader, opts *gluemetav1.ApplyOptions) error {
	decoder := yamlUtil.NewYAMLOrJSONDecoder(br, 100)

	var err error
	for {
		var rawObj runtime.RawExtension
		if err = decoder.Decode(&rawObj); err != nil {
			break
		}

		obj, gvk, err := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme).Decode(rawObj.Raw, nil, nil)

		if err != nil {
			return err
		}

		unstructuredMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
		if err != nil {
			return err
		}

		unstructuredObj := &unstructured.Unstructured{Object: unstructuredMap}

		gr, err := restmapper.GetAPIGroupResources(r.kcs.Discovery())
		if err != nil {
			return err
		}

		mapper := restmapper.NewDiscoveryRESTMapper(gr)
		mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
		if err != nil {
			return err
		}

		var dri dynamic.ResourceInterface
		if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
			if opts.NameSpace == `` {
				unstructuredObj.SetNamespace("default")
			} else {
				unstructuredObj.SetNamespace(opts.NameSpace)
			}

			dri = r.dc.Resource(mapping.Resource).Namespace(unstructuredObj.GetNamespace())
		} else {
			dri = r.dc.Resource(mapping.Resource)
		}

		if _, err := dri.Create(r.ctx, unstructuredObj, metav1.CreateOptions{FieldManager: "FieldManagerConflict"}); err != nil {
			return err
		}
	}

	if err != io.EOF {
		return err
	}

	return nil
}

func (r *SimpleK8sDriver) Delete(br io.Reader, opts *gluemetav1.DeleteOptions) error {
	decoder := yamlUtil.NewYAMLOrJSONDecoder(br, 100)

	var err error
	for {
		var rawObj runtime.RawExtension
		if err = decoder.Decode(&rawObj); err != nil {
			break
		}

		obj, gvk, err := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme).Decode(rawObj.Raw, nil, nil)

		if err != nil {
			return err
		}

		unstructuredMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
		if err != nil {
			return err
		}

		unstructuredObj := &unstructured.Unstructured{Object: unstructuredMap}

		gr, err := restmapper.GetAPIGroupResources(r.kcs.Discovery())
		if err != nil {
			return err
		}

		mapper := restmapper.NewDiscoveryRESTMapper(gr)
		mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
		if err != nil {
			return err
		}

		var dri dynamic.ResourceInterface
		if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
			if opts.NameSpace == `` {
				unstructuredObj.SetNamespace("default")
			} else {
				unstructuredObj.SetNamespace(opts.NameSpace)
			}
			dri = r.dc.Resource(mapping.Resource).Namespace(unstructuredObj.GetNamespace())
		} else {
			dri = r.dc.Resource(mapping.Resource)
		}

		if err := dri.Delete(r.ctx, unstructuredObj.GetName(), metav1.DeleteOptions{}); err != nil {
			return err
		}
	}

	if err != io.EOF {
		return err
	}

	return nil
}

func (r *SimpleK8sDriver) GetService(serviceName string, opts option.GetServiceOptions) (*data.SimpleService, error) {
	ns := `default`

	if opts.NameSpace != `` {
		ns = opts.NameSpace
	}

	svc, err := r.kcs.CoreV1().Services(ns).Get(r.ctx, serviceName, metav1.GetOptions{})
	return &data.SimpleService{Service: svc}, err
}

func (r *SimpleK8sDriver) ParseMainfest(mainfest string) (contaienrs []string) {
	yamls := strings.Split(mainfest, `---`)

	for _, yamlStr := range yamls {
		cts := getContainerNames(yamlStr)
		if len(cts) != 0 {
			contaienrs = append(contaienrs, cts...)
		}
	}

	return
}

func getContainerNames(yamlStr string) (containers []string) {
	rd := strings.NewReader(yamlStr)

	vp := viper.New()
	vp.SetConfigType(`yaml`)

	if err := vp.ReadConfig(rd); err != nil {
		return
	}

	var cts []corev1.Container
	switch vp.GetString("kind") {
	case `Pod`:
		pod := new(corev1.Pod)

		if err := vp.Unmarshal(pod); err != nil {
			return
		}

		cts = pod.Spec.Containers
	case `Deploy`:
		deploy := new(appsv1.Deployment)

		if err := vp.Unmarshal(deploy); err != nil {
			return
		}

		cts = deploy.Spec.Template.Spec.Containers
	default:
		return
	}

	for _, ct := range cts {
		containers = append(containers, ct.Name)
	}

	return
}

func (r *SimpleK8sDriver) ListNameSpace() (nsList []string, err error) {
	rlt, err := r.kcs.CoreV1().Namespaces().List(r.ctx, metav1.ListOptions{})
	if err != nil {
		return
	}

	for _, ns := range rlt.Items {
		nsList = append(nsList, ns.Name)
	}

	return

}

func (r *SimpleK8sDriver) LabelPod(
	namespace,
	podName string,
	labels map[string]string,
) error {
	patchData := map[string]interface{}{
		"metadata": map[string]map[string]string{
			"labels": labels,
		}}

	bt, err := json.Marshal(patchData)
	if err != nil {
		return err
	}

	_, err = r.labelPod(namespace, podName, bt)

	return err
}

func (r *SimpleK8sDriver) UnLabelPod(
	namespace,
	podName string,
	labels []string,
) error {
	lb := make(map[string]interface{})

	for _, key := range labels {
		lb[key] = nil
	}

	patchData := map[string]interface{}{
		"metadata": map[string]map[string]interface{}{
			"labels": lb,
		}}
	bt, err := json.Marshal(patchData)
	if err != nil {
		return err
	}

	_, err = r.labelPod(namespace, podName, bt)

	return err
}

func (r *SimpleK8sDriver) labelPod(
	namespace,
	podname string,
	data []byte,
) (*corev1.Pod, error) {
	return r.kcs.CoreV1().Pods(namespace).Patch(
		r.ctx,
		podname,
		types.StrategicMergePatchType,
		data,
		metav1.PatchOptions{},
	)
}
